瓦片预缓存 Sample详情

最后更新时间:2020年12月11日

功能介绍

在线地图服务是移动终端常用的出图方式,在线地图服务通常是以瓦片为出图单元,最终将瓦片按照一定的规则拼接出图。为了给用户提供流畅、高效、快速的地图浏览体验,MapGIS Mobile不仅采用了高性能的地图渲染引擎,还提供了自动缓存的策略。在加载在线瓦片地图时,系统会自动将浏览过的范围、级别的地图进行自动缓存,存储到移动设备中,再次显示该区域的地图时直接读取离线本地数据,不用请求在线服务,从而使得地图能够非常迅速地渲染展示。这是MapGIS Mobile提供的自动缓存策略,存在一定的局限性,对于没有浏览过的区域的地图不会进行缓存,这无法满足要求在断网情况下同样能够浏览任意级别、任意区域地图的场景。于是,瓦片预缓存功能应运而生。

为了提高在线瓦片地图的加载速度,地图瓦片可以在终端预生成本地缓存,客户端浏览地图时可直接读取,这种技术被称作为瓦片预缓存技术。对于移动端来说,受限于硬件水平、网络速度、存储空间等因素,实现与PC终端相同的地图浏览及交互体验却相当困难,瓦片预缓存技术便在一定程度上解决了上述问题;另一方面,瓦片预缓存在在线浏览地图时动态缓存到本地存储,当在离线的时候亦可以浏览相同地图,这在野外作业中尤为重要。

瓦片预缓存实现的原理:在网络通畅情况下,根据用户需求,按照范围、级别将在线地图瓦片在终端生成本地缓存瓦片,存储到本地数据库中。从而在网络未连接的情况下,客户端直接读取本地缓存瓦片,同样能够进行地图的展示浏览。

MapGIS Mobile支持多种来源的在线地图,其中能够使用瓦片预缓存功能的数据类型包括:MapGIS IGServer在线瓦片地图、WMS地图服务、WMTS地图服务、天地图、Google地图、百度地图、高德地图、OSM地图、Bing地图等。

在缓存时,可以设置地图的级别范围、地图范围来实现更精确、更有针对性的瓦片缓存。

功能接口

瓦片预缓存功能对应API为com.zondy.mapgis.map的ImageLayer类接口(10.3版本:com.zondy.mapgis.core.map.ServerLayer),核心接口如下所示:

接口 说明
ImageLayer.setTileCacheDir()【10.3:ServerLayer.setCacheLocation()】 设置缓存目录
ImageLayer.setTilePreFetchListener 设置预缓存状态监听
ImageLayer.prefetch() 预缓存指定范围、指定缩放级内的图像
无此接口【10.3:ServerLayer.stopFetch()】 停止预缓存
ImageLayer.deleteTileCache()【10.3:ServerLayer.clearCache()】 清除缓存
ImageLayer.setAccessMode() 设置地图服务访问模式(CacheOnly/ServerAndCache/ServerOnly)

实现方法

瓦片预缓存的大体实现方法如下所示:

(1)加载地图:首先需要加载能够进行瓦片预缓存功能的在线地图,并获取待进行缓存的ImageLayer图层对象;

(2)准备预缓存条件:选择需要缓存的地图范围、瓦片级别范围;

(3)设置监听:利用setTilePreFetchListener接口监听瓦片缓存的进度、状态;

(4)开始缓存:调用preFetch方法开始缓存;

(5)显示缓存:在缓存完成之后,可调用缓存进行显示。

实现过程

1

加载地图

加载在线瓦片地图,以IGServer瓦片地图服务为例。

    //缓存路径
    private String strCacheDir = FilePath.PHONE_SDCARD_PATH + "/MapGISSample/TileCache/";

    //服务图层1:igserver中的瓦片服务
    serverLayer1 = new ImageLayer();
    MapServer mapServer1 = ImageLayer.createMapServer(MapServer.MAPSERVER_TYPE_IGSERVER_TILE);
    //设置地图服务地址
    mapServer1.setURL("http://develop.smaryun.com:6163/igs/rest/mrms/tile/WorldJWTile");
    //为mapserver设置名称,须在setMapServer之前执行,创建的缓存文件会使用到此名称
    mapServer1.setName("IGServer瓦片服务");
    //设置缓存路径,必须在调用setMapServer之前执行,因为调用setMapServer的时候就会自动创建缓存了
    serverLayer1.setTileCacheDir(strCacheDir);
    //为服务图层设置地图服务
    serverLayer1.setMapServer(mapServer1);

    //地图对象
    map1 = new Map();
    map1.append(serverLayer1);
    //加载地图
    mapView.setMapAsync(map1, new MapViewFinishCallback() {
        @Override
        public void onDidFinish(boolean arg0) {
            if (arg0) {
                //地图加载成功
            }
        }
    });

代码说明:(1)进行瓦片预缓存的ImageLayer服务图层,需定义为成员变量。(2)设置缓存路径setTileCacheDir()必须要在调用setMapServer()之前执行,因为调用setMapServer()的时候就会自动创建缓存了。

2

准备预缓存条件

在进行瓦片缓存时,需要设置缓存的地图范围,由于瓦片地图采用了分级显示的机制,所以进行缓存时,也需要选择缓存的瓦片级别。在缓存时将会只缓存此地图范围内设置的级别的地图。设置的地图范围越小,缓存消耗时间越少,缓存后的文件数据量越小。选择的瓦片级别范围越多,消耗时间越多。级别越大的瓦片,缓存时消耗的时间越多。根据实际需求选择合理的地图范围、瓦片级别范围。

    //获取地图服务对象
    final MapServer mapServer = serverlayer.getMapServer();
    //连接数据,在缓存igserver中的瓦片服务的时候需要使用
    new Thread(new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            //igserver中的瓦片服务需要先连接数据,才能获取其信息
            long a = mapServer.connectData();
            if (a > 0) {
                Message m = new Message();
                m.what = 1;
                mhandler.sendMessage(m);
            } else {
                Message m = new Message();
                m.what = 0;
                mhandler.sendMessage(m);
               // Toast.makeText(TilePreFetch_Activity.this, "连接数据失败,请确保网络已连接", Toast.LENGTH_SHORT).show();
            }
        }
    }).start();

    mhandler = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            //转换为瓦片服务,获取最小最大级别
            if (mapServer instanceof TileMapServer) {
                TileMapServer tileServer = (TileMapServer) mapServer;
                //瓦片最小级别
                minZoom = tileServer.getMinZoom();
                //瓦片最大级别
                maxZoom = tileServer.getMaxZoom();
                //根据级别初始化
                initSpinner2(minZoom, maxZoom);
            }
        };
    };

3

设置缓存状态监听

在缓存之前,调用setTilePreFetchListener接口为服务图层设置缓存监听器,在其回调函数中可获取缓存的状态、进度、结果信息。需注意的是这些回调函数都是在子线程中执行的,所以如果需要讲缓存进度信息展示在视图中,需在主线程中处理。


    //设置预缓存状态监听类
    cacheServerLayer.setTilePreFetchListener(prefetchListener);
    //瓦片缓存监听器
    TilePrefetchListener prefetchListener = new TilePrefetchListener()
    {
        public double prefetch(ImageLayerEvent var1)
        {
            ImageLayerEventArgs args = var1.getArgs();
            //获取预缓存进度
            double dPercent = args.getPrefetchPercent();
            int dCurTileFetchProgress = (int)(dPercent*100);
            //设置界面显示瓦片缓存进度值
            progressBar.setProgress(dCurTileFetchProgress);
            Message msg = new Message();
            String str = " 更新进度:" + dCurTileFetchProgress +"%";
            msg.obj = str;
            msg.what = 0;
            mhandler2.sendMessage(msg);
            return dPercent;
        }
    };

4

开始缓存

准备工作完成后,可调用preFetch方法,指定缓存的最小最大级别、地图空间范围开始缓存,返回值为缓存的任务ID。缓存的级别可从第0级到第0级,下载的即为第0级的瓦片。在缓存时,需要确保网络状态良好,避免造成缓存失败的情况。

/**
 * 预缓存(异步方法)
 * 参数:预缓存的空间范围,预缓存的最小级别,预缓存的最大级别
 */
    new Thread(new Runnable() {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            bFetch = cacheServerLayer.prefetch(rect,minzoom,maxzoom);
            if (bFetch) {
                Message m = new Message();
                m.what = 1;
                mhandler2.sendMessage(m);
            }
        }
    }).start();

5

显示缓存

根据缓存监听可判断缓存结果,缓存成功完成后,会在缓存路径中生成缓存文件(如xx.mut文件)。缓存完成后,可调用离线缓存,在未连接网络时同样能够显示地图。

在调用缓存显示地图时,与普通的通过在线请求显示地图方法类似,但需调用setAccessMode方法设置从缓存中读取数据,关键代码如下所示:

    mhandler2 = new Handler() {
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            if (msg.what == 0) {
                String str = (String) msg.obj;
                cacheProgress.setText(str);
            }
            if (msg.what == 1) {
                if (msg.arg1 == 0) {
                    cacheProgress.setText("缓存完成");
                    Toast.makeText(TilePreFetch_Activity.this, "缓存成功。\n缓存路径:" + strCacheDir, Toast.LENGTH_SHORT).show();

                    //缓存完成之后重置范围
                    rect = new Rect();
                    //缓存图层对象
                    cacheServerLayer = serverLayer1;
                    //缓存完成之后,设置服务图层的数据只从缓存中读取
                    cacheServerLayer.setAccessMode(MapServerAccessMode.CacheOnly);
                }
                if (msg.arg1 == 1) {
                        cacheProgress.setText("缓存失败");
                }
            }
        };
    };